package com.chenqq.qsocket.handler;

import com.chenqq.qsocket.listener.SocketMessageListener;
import com.chenqq.qsocket.message.FileTask;
import com.chenqq.qsocket.message.SendFilePartMessage;
import com.chenqq.qsocket.tools.FileUtil;
import com.chenqq.qsocket.tools.GzipUtil;
import com.chenqq.qsocket.tools.LogShow;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.mina.core.session.IoSession;


public abstract class FileMessageHandler implements MessageHandler {
    protected static ConcurrentHashMap<Long, FileTask> mFileTasks = new ConcurrentHashMap<>();//发送端
    protected static ConcurrentHashMap<Long, FileTask> mRFileTasks = new ConcurrentHashMap<>();//接收端
    private static ExecutorService mExecutorService = Executors
            .newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    protected static String DOWNLOAD_PATH;//接受文件存储地址
    protected static String SEND_TEMPORARY_PATH;//发送文件时压缩文件存放地址
    protected SocketMessageListener socketMessageListener;
    protected String tag="";

    @Override
    public void handleListener(String tag,SocketMessageListener listener) {
        this.tag=tag;
        socketMessageListener=listener;
    }

    public static void setDownloadPath(String path, boolean isReceive) {
        if (isReceive) {
            DOWNLOAD_PATH = path;
        } else {
            SEND_TEMPORARY_PATH = path;
        }
        LogShow.showV("设置下载路径" + path);
    }


    protected FileTask removeFileTask(long id, boolean isReceive) {
        LogShow.showV("移除FileTask" + id);
        if (isReceive) {
            return mRFileTasks.remove(id);
        }
        return mFileTasks.remove(id);
    }


    public static ConcurrentHashMap<Long, FileTask> getFileTasks(boolean isReceive) {
        if (isReceive) {
            return mRFileTasks;
        }
        return mFileTasks;
    }

    protected void handleFileTaskException(long id, IoSession session, boolean isReceive) {
        session.closeNow();
        FileTask task = removeFileTask(id, isReceive);
        if (task != null) {
            task.running = false;
            if (isReceive||task.isZipped)
            FileUtil.deleteFile(task.zippedFilePath);
        }
        if (socketMessageListener!=null){
            socketMessageListener.showFileInfo(tag,session.toString());
        }
    }

    public static boolean zipFile(FileTask fileTask) {
        File tempFile = new File(SEND_TEMPORARY_PATH);
        if (!tempFile.exists()) {
            if (!tempFile.mkdirs()) {
                return false;
            } else {
                LogShow.showV("创建临时目录:" + SEND_TEMPORARY_PATH);
            }
        }
        // 将源文件压缩
        fileTask.zippedFilePath = SEND_TEMPORARY_PATH + File.separatorChar
                + System.currentTimeMillis() + "_Zip_" + fileTask.fileName;
        LogShow.showV("开始压缩原文件"+fileTask.isZipped);
        if (!fileTask.isZipped){
            fileTask.zippedFileSize=fileTask.size;
            fileTask.zippedFilePath=fileTask.filePath;
            return true;
        }
        long startTime = System.currentTimeMillis();
        try {
            GzipUtil.zipFile(fileTask.filePath, fileTask.zippedFilePath);
            fileTask.zippedFileSize = new File(fileTask.zippedFilePath)
                    .length();
            LogShow.showV("压缩结束 ， 耗时:" + (System.currentTimeMillis() - startTime) + " ms");
            long zippedFileSize = new File(fileTask.zippedFilePath).length();
            LogShow.showV("原文件大小:" + new File(fileTask.filePath).length()
                    + " 压缩后大小:" + zippedFileSize);
            fileTask.zippedFileSize = zippedFileSize;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }


    protected void sendFilePart(final FileTask fileTask, final IoSession session) {
        try {

            final int totalPart =  ((int) Math.ceil((double) fileTask.zippedFileSize / fileTask.fileSegmentSize));
            int partId = 0;
            LogShow.showV("总计" + totalPart + "个分段");

            while (partId < totalPart) {
                if (fileTask.running) {
                    mExecutorService.execute(new SendFilePartThread(partId,
                            fileTask, session, totalPart));
                }
                partId++;
            }

        } catch (Exception e) {
            e.printStackTrace();
            session.closeNow();
            handleFileTaskException(fileTask.id, session, false);
        }
    }

    private class SendFilePartThread implements Runnable {
        int pardIdLocal;
        FileTask fileTask;
        IoSession session;
        int totalPart;

        public SendFilePartThread(int partId, final FileTask fileTask,
                                  final IoSession session, int totalPart) {
            pardIdLocal = partId;
            this.fileTask = fileTask;
            this.session = session;
            this.totalPart = totalPart;
        }

        @Override
        public void run() {
            try {
                String filePate = fileTask.zippedFilePath ;
                RandomAccessFile randomAccessFile = new RandomAccessFile(
                        filePate, "rw");
                LogShow.showV("文件路径发送" + filePate);
                byte[] buffer = new byte[fileTask.fileSegmentSize];
                LogShow.showV("分段长度" + fileTask.fileSegmentSize);
                int availableSize;
                randomAccessFile.seek(pardIdLocal * fileTask.fileSegmentSize);
                availableSize = randomAccessFile.read(buffer);
                LogShow.showV("读取byte[]数值" + availableSize);
                randomAccessFile.close();
                if (availableSize == buffer.length) {
                    SendFilePartMessage sendFileMessage = new SendFilePartMessage(
                            fileTask.id, pardIdLocal, buffer);
                    session.write(sendFileMessage);
                } else {
                    byte[] actualPart = new byte[availableSize];
                    for (int i = 0; i < availableSize; i++) {
                        actualPart[i] = buffer[i];
                    }
                    SendFilePartMessage sendFileMessage = new SendFilePartMessage(
                            fileTask.id, pardIdLocal, actualPart);
                    session.write(sendFileMessage);
                }
                if (fileTask.partId.incrementAndGet() == totalPart - 1) {
                    LogShow.showV("发送完毕");
                    if (socketMessageListener!=null){
                        socketMessageListener.sendMessageSuccess(tag);
                    }
                    double sendTime = (System.currentTimeMillis() - fileTask.startTime2) / 1000d;
                    LogShow.showV("发送文件用时:" + sendTime + " s");
                    LogShow.showV("平均速度:" + ((new File(filePate).length() / 1024d) / sendTime) + " kbps");
                } else if (fileTask.partId.intValue() % 100 == 0) {
                    if (socketMessageListener!=null){
                        socketMessageListener.sendFileProgress(tag,fileTask.partId.intValue() *100/  totalPart);
                    }
                    LogShow.showV("tid:" + Thread.currentThread().getId() + " 发送进度: " + fileTask.partId.intValue() + " / " + totalPart);
                }
            } catch (Exception e) {
                e.printStackTrace();
                handleFileTaskException(fileTask.id, session, false);
            }
        }

    }


}
